Expand description
Serde support for querystring-style strings
Querystrings are not formally defined and loosely take the form of nested urlencoded queries.
This library aims for compatability with the syntax of
qs and also of the
Rack::Utils::parse_nested_query
implementation.
For users who do not require nested URL parameters, it is highly
recommended that the serde_urlencoded
crate is used instead, which
will almost certainly perform better for deserializing simple inputs.
Supported Types
At the top level, serde_qs
only supports struct
, map
, and enum
.
These are the only top-level structs which can be de/serialized since
Querystrings rely on having a (key, value) pair for each field, which
necessitates this kind of structure.
However, after the top level you should find all supported types can be de/serialized.
Note that integer keys are reserved for array indices. That is, a string of
the form a[0]=1&a[1]=3
will deserialize to the ordered sequence a = [1,3]
.
Usage
See the examples folder for a more detailed introduction.
Serializing/Deserializing is designed to work with maps and structs.
#[macro_use]
extern crate serde_derive;
extern crate serde_qs as qs;
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Address {
city: String,
postcode: String,
}
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct QueryParams {
id: u8,
name: String,
address: Address,
phone: u32,
user_ids: Vec<u8>,
}
let params = QueryParams {
id: 42,
name: "Acme".to_string(),
phone: 12345,
address: Address {
city: "Carrot City".to_string(),
postcode: "12345".to_string(),
},
user_ids: vec![1, 2, 3, 4],
};
let rec_params: QueryParams = qs::from_str("\
name=Acme&id=42&phone=12345&address[postcode]=12345&\
address[city]=Carrot+City&user_ids[0]=1&user_ids[1]=2&\
user_ids[2]=3&user_ids[3]=4")
.unwrap();
assert_eq!(rec_params, params);
Strict vs Non-Strict modes
serde_qs
supports two operating modes, which can be specified using
Config
.
Strict mode has two parts:
- how
serde_qs
handles square brackets - how
serde_qs
handles invalid UTF-8 percent decoded characters
Square Brackets
Technically, square brackets should be encoded in URLs as %5B
and %5D
.
However, they are often used in their raw format to specify querystrings
such as a[b]=123
.
In strict mode, serde_qs
will only tolerate unencoded square brackets
to denote nested keys. So a[b]=123
will decode as {"a": {"b": 123}}
.
This means that encoded square brackets can actually be part of the key.
a[b%5Bc%5D]=123
becomes {"a": {"b[c]": 123}}
.
However, since some implementations will automatically encode everything
in the URL, we also have a non-strict mode. This means that serde_qs
will assume that any encoded square brackets in the string were meant to
be taken as nested keys. From the example before, a[b%5Bc%5D]=123
will
now become {"a": {"b": {"c": 123 }}}
.
Non-strict mode can be useful when, as said before, some middleware automatically encodes the brackets. But care must be taken to avoid using keys with square brackets in them, or unexpected things can happen.
Invalid UTF-8 Percent Encodings
Sometimes querystrings may have percent-encoded data which does not decode
to UTF-8. In some cases it is useful for this to cause errors, which is how
serde_qs
works in strict mode (the default). Whereas in other cases it
can be useful to just replace such data with the unicode replacement
character (� U+FFFD
), which is how serde_qs
works in non-strict mode.
Flatten workaround
A current known limitation
in serde
is deserializing #[serde(flatten)]
structs for formats which
are not self-describing. This includes query strings: 12
can be an integer
or a string, for example.
We suggest the following workaround:
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_qs as qs;
extern crate serde_with;
use serde_with::{serde_as, DisplayFromStr};
#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct Query {
a: u8,
#[serde(flatten)]
common: CommonParams,
}
#[serde_as]
#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct CommonParams {
#[serde_as(as = "DisplayFromStr")]
limit: u64,
#[serde_as(as = "DisplayFromStr")]
offset: u64,
#[serde_as(as = "DisplayFromStr")]
remaining: bool,
}
fn main() {
let params = "a=1&limit=100&offset=50&remaining=true";
let query = Query { a: 1, common: CommonParams { limit: 100, offset: 50, remaining: true } };
let rec_query: Result<Query, _> = qs::from_str(params);
assert_eq!(rec_query.unwrap(), query);
}
Use with actix_web
extractors
The actix4
, actix3
or actix2
features enable the use of serde_qs::actix::QsQuery
, which
is a direct substitute for the actix_web::Query
and can be used as an extractor:
fn index(info: QsQuery<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
Support for actix-web 4.0
is available via the actix4
feature.
Support for actix-web 3.0
is available via the actix3
feature.
Support for actix-web 2.0
is available via the actix2
feature.
Use with warp
filters
The warp
feature enables the use of serde_qs::warp::query()
, which
is a substitute for the warp::query::query()
filter and can be used like this:
serde_qs::warp::query(Config::default())
.and_then(|info| async move {
Ok::<_, Rejection>(format!("Welcome {}!", info.username))
})
.recover(serde_qs::warp::recover_fn);
Modules
- Functionality for using
serde_qs
withactix_web
. - Functionality for using
serde_qs
withwarp
.
Structs
- To override the default serialization parameters, first construct a new Config.
Enums
- Error type for
serde_qs
.
Functions
- Deserializes a querystring from a
&[u8]
. - Deserializes a querystring from a
&str
. - Serializes a value into a querystring.
- Serializes a value into a generic writer object.